#include "i2cmaster.h"
#include <util/delay.h>
#include <avr/interrupt.h>
#include "clock.h"
#include "system.h"

volatile uint8 time_changed_flag = 0;


uint8 bin2bcd(uint8 x){
	return (x%10) | ((x/10) << 4);
}

uint8 bcd2bin(uint8 x){
	return (x >> 4) * 10 + (x & 0x0f);
}

int16 getPastHours(uint8 h1, uint8 d1, uint8 m1, uint8 y1,
				   uint8 h2, uint8 d2, uint8 m2, uint8 y2){
	int16 h = h1 - h2; //result variable

	//calculate timespan since last metering in h
	//if month == FEBRUAR AND y%4==0 29!
	if(d2 != d1 ||
	   m2 != m1 ||
	   y2 != y1 )
	{		
		if(d2 >  d1 &&		//only day differs
		   m2 == m1 &&
		   y2 == y1)						h += (d2-d1) * 24;
		else
		if(m2 >  m1 &&
		   y2 == y1){
		   															//add left days of last_inj_m
		   															h += (daysPerMonth[m1] - d1) * 24;
																	//add hour of every intermediate month
		   for(uint8 j=m1+1;j<m2;j++)								h += daysPerMonth[j] * 24;
		   															//add days of current month - current day
																	h += d2 * 24;
		}else
		//Max difference in years is 1!
		if(y2 > y1){												//add left days of last_inj_m
		   															h += (daysPerMonth[m1] - d1) * 24;
																	//add hour of every intermediate month in last_inj_year
		   for(uint8 j=m1+1;j<12;j++)								h += daysPerMonth[j] * 24;
		   															//add hour of every intermediate month in current year
		   for(uint8 j=1;j<m2;j++)									h += daysPerMonth[j] * 24;
		   															//add days of current month
																	h += d2 * 24;
		}else{
			h = -getPastHours(h2, d2, m2, y2,
				   			  h1, d1, m1, y1);
		}
	}

	return h;
}

void setTime(uint8 h, uint8 min){
	uint8 o_h, o_min;
	int8  diff_min, diff_h;
	getTime(&o_h, &o_min);

	i2c_start_wait(DevDS1307+I2C_WRITE);
	i2c_write(0x00);   						// Start address: Timekeeper Register 00h
	i2c_write(0x00);                        // bit 7 (ch-bit) 0 (1 stops the clock?), rest seconds (0 seconds)
    i2c_write(bin2bcd(min));		       	// minutes
	i2c_write(bin2bcd(h));					// hours
    i2c_stop();

	diff_h = h - o_h;
	diff_min = min - o_min;
	//new time is before actual time
	if(diff_h < 0){
		if(diff_min > 0){
			diff_min = -(60 - diff_min);
			diff_h++;
		}
	}
	//new time is after actual time
	else{
		if(diff_min < 0 && diff_h > 0){
			diff_min = 60 + diff_min;
		//	diff_h--;
		}
	}

	//update last injection time to ensure, that the interval is right
	for(uint8 i=0;i<7;i++){	
		if(allSlotValues[i].last_inj_min+diff_min >= 0)
		{														allSlotValues[i].last_inj_min = (allSlotValues[i].last_inj_min+diff_min)%60;
			if((allSlotValues[i].last_inj_min+diff_min)/60 > 0
				&& diff_h < 0)									allSlotValues[i].last_inj_h++;
		}			
		else{													allSlotValues[i].last_inj_min = 60 + allSlotValues[i].last_inj_min + diff_min;
																allSlotValues[i].last_inj_h--;
		}
		if(allSlotValues[i].last_inj_h + diff_h >= 0)			allSlotValues[i].last_inj_h   = (allSlotValues[i].last_inj_h + diff_h)%24;
		else													allSlotValues[i].last_inj_h   = 24 + allSlotValues[i].last_inj_h + diff_h;
	}
	saveAllValues(allSlotValues);
}

void setDate(uint8 d, uint8 m, uint8 y){
	uint8 o_y, o_m, o_d, o_h, o_min;
	getTimeAndDate(&o_y, &o_m, &o_d, &o_h, &o_min);
	int16 d_diff, last_inj_m, last_inj_d, last_inj_y;

	i2c_start_wait(DevDS1307+I2C_WRITE);
	i2c_write(0x04);   						// start-address is DATE field
	i2c_write(bin2bcd(d));					// day of month
	i2c_write(bin2bcd(m));					// month
	i2c_write(bin2bcd(y));					// Jahr einstellen
//	i2c_write(0x90);						// control register OUT (bit  = SQWE = 1, Rest 0)
    i2c_stop();

	//alter the time of last injection
	//so that set date has no influence on the next scheduled injection time
	for(uint8 i=0;i<7;i++){
		//get old time difference
		d_diff =  getPastHours(allSlotValues[i].last_inj_h,
							   allSlotValues[i].last_inj_d,
							   allSlotValues[i].last_inj_m,
							   allSlotValues[i].last_inj_y,
							   o_h, o_d, o_m, o_y)/24;

		last_inj_m = m;
		last_inj_d = d-d_diff;
		last_inj_y = y;
		while(last_inj_d < 0){
			last_inj_m--;
			if(last_inj_m < 0){
				last_inj_m = 12;
				last_inj_y--;
			}
			last_inj_d = daysPerMonth[last_inj_m] + last_inj_d;
		}
		allSlotValues[i].last_inj_d = last_inj_d;
		allSlotValues[i].last_inj_m = last_inj_m;
		allSlotValues[i].last_inj_y = last_inj_y;
	}
	saveAllValues(allSlotValues);
}

void initClock(){
	unsigned char byte00h;
	unsigned char ack;

	//get byte 00h from register
	i2c_start_wait(DevDS1307 + I2C_WRITE);
	i2c_write(0x00);   						// address 00
	i2c_rep_start(DevDS1307 + I2C_READ);		// read
    byte00h = i2c_readNak();
	i2c_stop();

	
	// clock enabled (ch-bit 0) or ch-bit 1 (disabled)?
	if (byte00h & 0x80) {

		// Enforces bit 7 to be 0, seconds (bits 0-6) stay the same
		byte00h= byte00h & 0x7F;

		i2c_start_wait(DevDS1307 + I2C_WRITE);
		i2c_write(0x00);
		i2c_write(byte00h);
		i2c_stop(); 
	}

	//init daysPerMonth-array
	//daysPerMonth[0] = 0
	daysPerMonth[0] = 0;
	daysPerMonth[1] = 31;
	daysPerMonth[2] = 28;
	daysPerMonth[3] = 31;
	daysPerMonth[4] = 30;
	daysPerMonth[5] = 31;
	daysPerMonth[6] = 30;
	daysPerMonth[7] = 31;
	daysPerMonth[8] = 31;
	daysPerMonth[9] = 30;
	daysPerMonth[10] = 31;
	daysPerMonth[11] = 30;
	daysPerMonth[12] = 31;

}

void getTimeAndDate(uint8* y, uint8* m, uint8* d, uint8 *h, uint8 *min){
	unsigned char rtc_reg[8];
	// write ok, read value back from Device,  wait until 
    // the device is no longer busy from the previous write operation 
	i2c_start_wait(DevDS1307+I2C_WRITE);    // Startcondition wait for Busy
	i2c_write(0x00);   						// Register Pointer
	i2c_rep_start(DevDS1307+I2C_READ);		// Repeat Start
    rtc_reg[0] = i2c_readAck();             // Read Byte 0
	rtc_reg[1] = i2c_readAck();
	rtc_reg[2] = i2c_readAck();
	rtc_reg[3] = i2c_readAck();
	rtc_reg[4] = i2c_readAck();
	rtc_reg[5] = i2c_readAck();
	rtc_reg[6] = i2c_readAck();
	rtc_reg[7] = i2c_readNak();				// Read Byte 7, Da Byte7 als letztes gelesen wird muss dieses mit NACK abgeschlossen werden!
    i2c_stop();

	*y	 	= bcd2bin(rtc_reg[6]); //year in current century
	*m 		= bcd2bin(rtc_reg[5]);
	*d	 	= bcd2bin(rtc_reg[4]);
	*h 		= bcd2bin(rtc_reg[2] & 0x3F);
	*min 	= bcd2bin(rtc_reg[1]);


}

void getTime(uint8 *h, uint8 *min){
	unsigned char rtc_reg[8];
	// write ok, read value back from Device,  wait until 
    // the device is no longer busy from the previous write operation 
	i2c_start_wait(DevDS1307+I2C_WRITE);    // Startcondition wait for Busy
	i2c_write(0x00);   						// Register Pointer
	i2c_rep_start(DevDS1307+I2C_READ);		// Repeat Start
    rtc_reg[0] = i2c_readAck();             // Read Byte 0
	rtc_reg[1] = i2c_readAck();
	rtc_reg[2] = i2c_readAck();
	rtc_reg[3] = i2c_readAck();
	rtc_reg[4] = i2c_readAck();
	rtc_reg[5] = i2c_readAck();
	rtc_reg[6] = i2c_readAck();
	rtc_reg[7] = i2c_readNak();				// Read Byte 7, Da Byte7 als letztes gelesen wird muss dieses mit NACK abgeschlossen werden!
    i2c_stop();

	*h 		= bcd2bin(rtc_reg[2] & 0x3F);
	*min 	= bcd2bin(rtc_reg[1]);
}
